package prj.common.service;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.util.Random;

import javax.faces.context.FacesContext;
import javax.servlet.http.HttpSession;

import org.springframework.stereotype.Service;

import prj.common.exception.EmptyCaptchaException;
import prj.common.exception.InvalidCaptchaException;
@Service
public class CaptchaService {
	public static final String CAPTCHA_SESSION_KEY = "CAPTCHA_SESSION_KEY.";
	private static final char[] CAPTCHA_POSSIBLE_CHARS = new char[] {
		'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
		'p','q','r','s','t','u','v','w','x','y','z','1','2','3','4',
		'5','6','7','8','9','0','A','B','C','D','E','F','G','H','I',
		'J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'
	};
	private static final double PI = 3.1415926535897932384626433832795;
	
	public static BufferedImage generateCaptchaImage(String captchaText, int width, int height) {
	    Random r = new Random();
		BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_INDEXED);
		Graphics2D gd = bi.createGraphics();
		gd.fillRect(0, 0, width, height);
		//Add polka dots
		for ( int k = 0; k < 15; k++ ) 
		{ 
		    gd.setColor(new Color(r.nextInt(255),r.nextInt(255),r.nextInt(255)));
		    int iRadius = (int) (Math.random() * height / 2.0); 
		    int iX = (int) (Math.random() * width - iRadius); 
		    int iY = (int) (Math.random() * height - iRadius); 
		    gd.fillRoundRect(iX, iY, iRadius * 2, iRadius * 2,100,100); 
		} 		
		//Draw the text
		Font font = new Font("Verdana", Font.BOLD, 20);
		RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
	    rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);		
	    gd.setRenderingHints(rh);
		gd.setColor(Color.BLACK);
	    /*
	    TextLayout tl = new TextLayout(captchaText,font,gd.getFontRenderContext());
	    tl.draw(gd, 20, 40);*/
	    gd.setFont(font);
	    for (int j = 0; j < captchaText.length(); j++) {
	    	//gd.setColor(new Color(r.nextInt(255),r.nextInt(255),r.nextInt(255)));
	        if (j%2==0) gd.drawString(captchaText.substring(j,j+1),25*j,24); 
	        else gd.drawString(captchaText.substring(j,j+1),25*j,35); 	   
	    }
	    
	    //Apply shearing
	    int periodValue = 20;
	    int nofFrames = 15;
	    int phaseNumber = 7;
	    double deltaX;
	    double deltaY;
	    
	    for (int i = 0; i < width; ++i) {
	    	deltaX = getDelta(periodValue, i, phaseNumber, nofFrames);
	    	gd.copyArea(i, 0, 1, height, 0, (int) deltaX);
	    	//gd.drawLine(i, (int) deltaX, i, 0);
	    	//gd.drawLine(i, (int) deltaX + height, i, width);
	    }

	    for (int i = 0; i < height; ++i) {
	    	deltaY = getDelta(periodValue, i, phaseNumber, nofFrames);
	    	gd.copyArea(i, 0, 1, width, 0, (int) deltaY);
	    	//gd.drawLine(i, (int) deltaY, i, 0);
	    	//gd.drawLine(i, (int) deltaY + width, i, width);
	    }
	    
	    //Draw a broken line
	    /*int yp1 = r.nextInt(height);
	    int yp2 = r.nextInt(height);
	    int yp3 = r.nextInt(height/2);
	    int yp4 = r.nextInt(height);
	    int yp5 = r.nextInt(height);
	    
	    drawThickLineOnImage(gd, 0, yp1, width / 4, yp2);
	    drawThickLineOnImage(gd, width / 4, yp2, width / 2, yp3);
	    drawThickLineOnImage(gd, width / 2, yp3, 3 * width / 4, yp4);
	    drawThickLineOnImage(gd, 3 * width / 4, yp4, width, yp5);*/		

	    return bi;
	}
	
	private static void drawThickLineOnImage(Graphics graphics, int x1, int y1, int x2, int y2) {
	    int dX = x2 - x1;
	    int dY = y2 - y1;
	    int xPoints[] = new int[4];
	    int yPoints[] = new int[4];
	    int thickness = 2;
	
	    double lineLength = Math.sqrt(dX * dX + dY * dY);
	    double scale = (double) (thickness) / (2 * lineLength);
	    double ddx = -scale * (double) dY;
	    double ddy = scale * (double) dX;
	
	    graphics.setColor(Color.black);
	
	    ddx += (ddx > 0) ? 0.5 : -0.5;
	    ddy += (ddy > 0) ? 0.5 : -0.5;
	    dX = (int) ddx;
	    dY = (int) ddy;
	
	    xPoints[0] = x1 + dX;
	    yPoints[0] = y1 + dY;
	    xPoints[1] = x1 - dX;
	    yPoints[1] = y1 - dY;
	    xPoints[2] = x2 - dX;
	    yPoints[2] = y2 - dY;
	    xPoints[3] = x2 + dX;
	    yPoints[3] = y2 + dY;
	
	    graphics.fillPolygon(xPoints, yPoints, 4);
	}
	private static double getDelta(int period, double i, double phase, double frames) {
		return (double) (period / 2) * Math.sin(i / (double) period + (2 * PI * phase) / frames);
	}
	public static String getCaptchaString() {
		String captchaText = "";
		Random random = new Random();
		for (int i = 0; i < 5; i++) {
			captchaText += CAPTCHA_POSSIBLE_CHARS[random.nextInt(CAPTCHA_POSSIBLE_CHARS.length)];
		}
		return captchaText;
	}
	public void validateCaptcha(String enteredCaptcha, String key)
		throws EmptyCaptchaException,
				InvalidCaptchaException {
		String sessionValue = (String)  ((HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(true)).getAttribute(CAPTCHA_SESSION_KEY + key);
		if (enteredCaptcha.trim().length() == 0) throw new EmptyCaptchaException();
		else if (!sessionValue.equalsIgnoreCase(enteredCaptcha)) throw new InvalidCaptchaException();
	}
}
